Skip to content

Feat/budget limits#82

Merged
RadCod3 merged 3 commits into
mainfrom
feat/budget-limits
Jun 3, 2026
Merged

Feat/budget limits#82
RadCod3 merged 3 commits into
mainfrom
feat/budget-limits

Conversation

@RadCod3

@RadCod3 RadCod3 commented Jun 2, 2026

Copy link
Copy Markdown
Owner

Implements #74

@coderabbitai

coderabbitai Bot commented Jun 2, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@RadCod3, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 47 minutes and 28 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a48f258b-a4fa-4923-867d-5fa804848a2a

📥 Commits

Reviewing files that changed from the base of the PR and between 238078c and b24a44e.

📒 Files selected for processing (3)
  • src/lampyrid/models/lampyrid_models.py
  • src/lampyrid/services/budgets.py
  • tests/integration/test_budgets.py
📝 Walkthrough

Walkthrough

This PR adds full CRUD (create, read, update, delete) support for budget limits in LamPyrid. It introduces data models, Firefly HTTP client methods, service-layer business logic, MCP tool registration, and comprehensive test coverage spanning unit and integration tests.

Changes

Budget Limit CRUD Operations

Layer / File(s) Summary
Budget limit data models and request contracts
src/lampyrid/models/lampyrid_models.py
BudgetLimit parses from Firefly's BudgetLimitRead with computed spent aggregation. Request models SetBudgetLimitRequest, ListBudgetLimitsRequest, and DeleteBudgetLimitRequest share base validation requiring exactly one of budget_id/budget_name and enforcing paired/ordered date constraints.
Firefly HTTP client CRUD methods
src/lampyrid/clients/firefly.py
Low-level async methods create_budget_limit (POST), update_budget_limit (PUT), and delete_budget_limit (DELETE) call Firefly III /budgets/{budget_id}/limits endpoints with serialized payloads and return typed responses or boolean for deletion.
Service layer budget limit operations
src/lampyrid/services/budgets.py
Implements helpers _resolve_budget_id (case-insensitive name/ID matching), _resolve_period (dates or default to current month), _find_limit_for_period (lookup by exact dates). Public methods set_budget_limit (idempotent upsert), list_budget_limits (with filtering), and delete_budget_limit (with validation) coordinate resolution, lookup, and client calls.
MCP tool registration and handlers
src/lampyrid/tools/budgets.py
Registers three FastMCP tools: set_budget_limit (idempotent), list_budget_limits (read-only), and delete_budget_limit (destructive), each delegating to corresponding BudgetService methods.
Test fixtures and request factories
tests/conftest.py, tests/fixtures/budgets.py
Cleanup fixture budget_limit_cleanup tracks created limits for post-test deletion. Request factories make_set_budget_limit_request, make_list_budget_limits_request, make_delete_budget_limit_request construct objects with current-month defaulting; helpers _default_month and _current_month compute date ranges.
Unit tests for service and tool annotations
tests/unit/test_budgets_service.py, tests/unit/test_tool_annotations.py
Service unit tests verify set_budget_limit creation/upsert, period defaulting, name resolution, list_budget_limits mapping with spent parsing, and delete_budget_limit success/failure cases. Updated _assert_mutating_tool validates tool idempotency and destructiveness flags.
End-to-end integration tests
tests/integration/test_budgets.py
Seven tests verify creation, upsert, name-based resolution, listing, interaction with spending calculations, and deletion with cleanup verification using the fixture and factory functions.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • RadCod3/LamPyrid#58: Introduces service-layer refactoring that this PR builds upon for budget limit operations.
  • RadCod3/LamPyrid#35: Both PRs aggregate attrs.spent entries to compute budget limit spending; this PR adds the BudgetLimit CRUD plumbing that complements the spending calculation logic.

Poem

🐰 Budget limits, wisely drawn with date and name,
Upserts idempotent, deletions sure and tame,
From Firefly III's streams to service logic clean,
Through MCP tools exposed, a budget keeper's dream!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Feat/budget limits' is related to the changeset, which implements comprehensive budget limit functionality across models, services, clients, and tools.
Description check ✅ Passed The description 'Implements #74' is related to the changeset and references the corresponding issue, though it lacks implementation detail.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/budget-limits

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov-commenter

codecov-commenter commented Jun 2, 2026

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 94.16667% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 97.53%. Comparing base (fa750c1) to head (b24a44e).

Files with missing lines Patch % Lines
src/lampyrid/services/budgets.py 92.59% 4 Missing ⚠️
src/lampyrid/models/lampyrid_models.py 92.50% 3 Missing ⚠️
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #82      +/-   ##
==========================================
- Coverage   97.66%   97.53%   -0.14%     
==========================================
  Files          20       20              
  Lines        3130     3248     +118     
==========================================
+ Hits         3057     3168     +111     
- Misses         73       80       +7     
Flag Coverage Δ
integration 97.53% <94.16%> (-0.14%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/lampyrid/services/budgets.py (1)

281-287: ⚡ Quick win

Paired start/end dates are already enforced by the request models

_resolve_period falls back to the current month unless both start_date and end_date are non-None (src/lampyrid/services/budgets.py:281-287). However, the request models that reach this helper (SetBudgetLimitRequest / DeleteBudgetLimitRequest, via _BudgetLimitRequestBase) reject mismatched inputs by raising ValueError('Provide both start_date and end_date, or neither.') when only one is provided (src/lampyrid/models/lampyrid_models.py:833-840), so the “silently discarded single date” path should be unreachable.

Defense-in-depth: consider making _resolve_period explicitly raise when exactly one argument is non-None instead of defaulting.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lampyrid/services/budgets.py` around lines 281 - 287, _resolve_period
currently defaults to the current month when one of start_date/end_date is
provided and the other is None; update it to raise a ValueError when exactly one
of start_date or end_date is non-None to enforce the same validation already
present in the request models (SetBudgetLimitRequest, DeleteBudgetLimitRequest
via _BudgetLimitRequestBase). Locate the _resolve_period function and replace
the silent fallback branch with an explicit check that raises
ValueError('Provide both start_date and end_date, or neither.') when (start_date
is None) != (end_date is None), otherwise keep the existing behavior for both
provided or both None.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lampyrid/models/lampyrid_models.py`:
- Around line 790-791: The model currently substitutes silent defaults for
required fields by using budget_id=attrs.budget_id or '' and
amount=float(attrs.amount) if attrs.amount is not None else 0.0; update the
model to preserve None instead of masking missing data by changing the field
type annotations to Optional[str] and Optional[float] and remove the fallback
substitutions in the factory/constructor code that consumes attrs (leave
budget_id as attrs.budget_id and set amount to float(attrs.amount) only when
attrs.amount is not None, otherwise None); ensure the class-level type hints and
the classmethod that builds instances from attrs are updated together to reflect
Optional types.

In `@tests/integration/test_budgets.py`:
- Around line 469-489: The test test_delete_budget_limit creates a budget limit
via mcp_client.call_tool('set_budget_limit') but doesn't register it with the
budget_limit_cleanup fixture, so a failed delete/assert can leak state; after
the set_budget_limit call (the created limit), register that created limit with
the budget_limit_cleanup fixture (e.g., call budget_limit_cleanup.register or
the fixture's add method with the created limit id/period) so the fixture will
remove the limit on teardown if the in-test deletion fails.

---

Nitpick comments:
In `@src/lampyrid/services/budgets.py`:
- Around line 281-287: _resolve_period currently defaults to the current month
when one of start_date/end_date is provided and the other is None; update it to
raise a ValueError when exactly one of start_date or end_date is non-None to
enforce the same validation already present in the request models
(SetBudgetLimitRequest, DeleteBudgetLimitRequest via _BudgetLimitRequestBase).
Locate the _resolve_period function and replace the silent fallback branch with
an explicit check that raises ValueError('Provide both start_date and end_date,
or neither.') when (start_date is None) != (end_date is None), otherwise keep
the existing behavior for both provided or both None.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3711c58e-ef30-490d-9835-c1de8530413f

📥 Commits

Reviewing files that changed from the base of the PR and between fa750c1 and 238078c.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (9)
  • src/lampyrid/clients/firefly.py
  • src/lampyrid/models/lampyrid_models.py
  • src/lampyrid/services/budgets.py
  • src/lampyrid/tools/budgets.py
  • tests/conftest.py
  • tests/fixtures/budgets.py
  • tests/integration/test_budgets.py
  • tests/unit/test_budgets_service.py
  • tests/unit/test_tool_annotations.py

Comment thread src/lampyrid/models/lampyrid_models.py Outdated
Comment thread tests/integration/test_budgets.py Outdated
- BudgetLimit.from_budget_limit_read: drop silent defaults for the
  required budget_id/amount fields so missing API data fails loudly
  instead of becoming '' / 0.0
- _resolve_period: raise when exactly one of start/end is provided
  (defense-in-depth; mirrors request-model validation)
- test_delete_budget_limit: register the created limit with
  budget_limit_cleanup as a teardown safety net
@RadCod3 RadCod3 force-pushed the feat/budget-limits branch from a5563e1 to b24a44e Compare June 2, 2026 18:44
@RadCod3 RadCod3 merged commit e32a881 into main Jun 3, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants